systemctl — systemd 系统与服务管理器控制详解

systemctl 是 Linux 下 systemd 系统与服务管理器(system and service manager)的控制前端。它并非直接操作进程,而是作为客户端通过 D-Bus 总线与 PID 1 的 systemd 守护进程通信,用于查看和改变系统、服务的状态。几乎每一个"我要管理这个 Linux 系统的进程与服务"的操作,都通过 systemctl 完成。

1. 概述

1.1 systemctl 是什么

systemctl 的功能分为两大块:

命令语法:systemctl [OPTIONS...] COMMAND [UNIT...],默认与系统管理器通信;加 --user 则与当前用户的用户管理器通信。

核心定位:systemctl 只是"遥控器",真正的执行者是 systemd(PID 1)。所有状态管理都通过 D-Bus 接口 org.freedesktop.systemd1 完成,因此你可以在脚本中通过 systemctl show 读取机器可解析的属性,而不是解析人读输出。
专有名词:

1.2 单元文件的查找路径

systemd 按优先级从高到低在以下目录查找单元文件,同名文件优先级高的生效:

目录优先级用途
/etc/systemd/system/高(持久)管理员自定义/覆盖(enable 的符号链接也在这里)
/run/systemd/system/高(临时)本次开机有效,重启即失(--runtime)
/usr/lib/systemd/system/低(厂商)软件包自带的服务定义

三者是"栈式叠加":高优先级文件未定义的项会回落到低优先级文件,而非整体替换。每处目录都可存在 foo.service.d/ 子目录存放 drop-in 片段。

1.3 enable 与 start 是正交的

这是新手最容易混淆的点:

用 systemctl enable --now foo 可一步完成两件事;同理 disable --now = 取消自启 + 停止。

systemctl enable nginx      # 只设置开机自启
systemctl start nginx       # 只立即启动
systemctl enable --now nginx   # 自启 + 立即启动

1.4 定位:systemd 只是 Linux 的主流 init 系统,并非全部

systemctl 只对 systemd 有效。而 Linux 世界并非全员 systemd,Apple 系则是另一套体系。先分清"面前的机器用哪个 init 系统"再选命令,否则会得到 command not found。

家族典型系统init / 服务管理器说明
Linux(主流)Ubuntu / Debian / Fedora / RHEL / Arch / openSUSEsystemd覆盖 90%+ 的服务器与桌面发行版
Linux(例外)AlpineOpenRCDocker 官方镜像默认,容器里最常见
Linux(例外)Gentoo / VoidOpenRC / runit极简派发行版
Linux(例外)Devuan / antiXSysVinit反 systemd 分支,回归传统 init
Linux(容器)极简镜像 / scratch无 init由 Docker / Podman 运行时直接管理进程
ApplemacOS / iOS / iPadOSlaunchd自 Mac OS X 10.4 起统一,无备选

Apple 系收敛,Linux 系多元:launchd 是苹果的唯一标准;systemd 只是 Linux 的主流而非全员。选命令前先确认:systemctl(systemd)、rc-service(OpenRC)、service(SysVinit)、launchctl / brew services(macOS)。

systemd 与 launchd 概念对照

systemd / systemctllaunchd / launchctl用途
systemctl start foobrew services start foo(写 plist 后 launchctl bootstrap)启动服务
systemctl enable fooplist 中 RunAtLoad=true(开机自启)开机自启
systemctl status foobrew services list / launchctl list查看状态
单元文件 /etc/systemd/system/foo.serviceplist ~/Library/LaunchAgents/*.plist服务定义
journalctl -u foolaunchd / 系统日志日志
记忆锚点:brew services 只是 launchd 的封装(写 homebrew.mxcl.<name>.plist 到 LaunchAgents 再用 launchctl 注册);而 systemctl 是 systemd 的 D-Bus 客户端。二者都是"遥控器",一个遥控 launchd,一个遥控 systemd。

2. 行为图(系统"做什么")

2.1 用例图 — systemctl 交互全景

管理员(root)是主要使用角色;普通用户可用 --user 管理自己的用户服务;后台的 systemd 则是这些操作最终作用的被控制对象。

PlantUML diagram
图 1:systemctl 用例图(行为全景)

用例演示

systemctl list-units --type=service --state=running   # 列出所有运行中的服务
systemctl status nginx                                # 查看 nginx 详细状态+最近日志
systemctl daemon-reload                               # 单元文件改动后重载配置
systemctl set-property nginx.service MemoryMax=2G     # 运行时调整资源上限
专有名词:activating / active / deactivating / inactive / failed 是单元的激活(active)状态;loaded / not-found / masked / error 是单元的加载(load)状态。二者是不同维度,如一个单元可以"已加载但未激活"。

2.2 活动图 — 服务生命周期主流程

以"启动一个服务 → 观察 → 停止"为主线,展示 systemctl 与 systemd 的职责划分。

PlantUML diagram
图 2:服务生命周期主流程活动图(带泳道)

用例演示

systemctl start redis.service     # 启动
systemctl status redis.service    # 观察 active (running)
systemctl stop redis.service      # 优雅停止(SIGTERM)
# 若进程未在 TimeoutStopSec 内退出,systemd 会升级为 SIGKILL
专有名词:ExecStart 是服务启动时执行的命令行;Type=simple / notify / forking / oneshot 决定 systemd 如何判定"启动完成"——simple 表示 fork 出进程即完成,notify 表示等服务通过 sd_notify 上报 READY,forking 表示父进程退出、子进程接管,oneshot 表示进程执行完即视为一次完成(配合 RemainAfterExit)。

2.3 状态机图 — unit 生命周期

单元在 systemd 眼中的生命周期,对应 systemctl status 里 Active: 一行的取值。

PlantUML diagram
图 3:unit 生命周期状态机图

ACTIVE 状态速查

状态含义
active已启动/绑定/挂载(视单元类型而定)
inactive已停止/解绑
failed启动失败或运行中崩溃(退出码非零、超时、重启次数超限)
activating从 inactive 走向 active 的过程中
deactivating从 active 走向 inactive 的过程中
reloadingactive 且正在重载配置

2.4 时序图 — systemctl start 内部调用

一次 systemctl start nginx.service,在 systemctl、D-Bus、systemd 与服务进程之间如何流转。

PlantUML diagram
图 4:systemctl start 内部时序图

用例演示

# 观察事务中实际入队了哪些 job
systemctl start --show-transaction nginx.service

# 观察管理器日志(另一终端)
journalctl -u nginx.service -f
专有名词:StartUnit(name, mode) 是 systemd 管理器的 D-Bus 方法;事务 transaction 指一次请求连带的所有依赖 job 集合;job mode(replace / fail / isolate 等)决定与已有任务冲突时的处理策略。

2.5 活动图 — enable / start / mask 对比

把三者放一起对比,理解它们对"开机自启"和"立即运行"两个维度的影响。

PlantUML diagram
图 5:enable / start / mask / disable 对比活动图

用例演示

systemctl enable  nginx        # 创建自启符号链接
systemctl disable nginx        # 移除自启符号链接(不影响当前运行)
systemctl mask    nginx        # 链接到 /dev/null,任何 start/enable 都失败
systemctl unmask  nginx        # 解除屏蔽
systemctl is-enabled nginx     # 查看自启状态(enabled/disabled/masked/static...)
专有名词:[Install] 段是单元文件里描述"如何被 enable"的段落,核心是 WantedBy=multi-user.target,表示 enable 时在对应 target 的 .wants/ 目录建符号链接;WantedBy vs RequiredBy 分别对应软依赖(失败不影响 target)与硬依赖。

3. 结构图(系统"由什么组成")

3.1 部署图 — systemd 生态

systemctl 只是 systemd 生态众多管理工具中的一个,它们围绕 D-Bus 总线协作。

PlantUML diagram
图 6:systemd 生态部署图

用例演示

systemctl list-machines             # 列出本机与本地容器
systemctl --host=user@host status   # SSH 远程管理
systemctl -M container status       # 管理容器内服务
systemctl --user status             # 管理当前用户服务
专有名词:systemd-journald 是二进制日志守护进程(journalctl 的读取对象);systemd-logind 管理登录会话与电源按键(inhibitor 锁);systemd-udevd 负责设备热插拔与命名;systemd --user 是每个登录用户独立的用户管理器;--host / -M 让 systemctl 跨主机/容器操作。

3.2 组件图 — systemctl 内部

把 systemctl 进程拆开看:它如何解析你的命令、如何与 systemd 通信、如何读写单元文件。

PlantUML diagram
图 7:systemctl 内部组件图

用例演示

systemctl status /home        # 路径自动转换为 home.mount 单元
systemctl status /dev/sda     # 设备路径 → dev-sda.device
systemctl stop "sshd@*.service"   # 通配符匹配所有 sshd@.service 实例
systemctl edit nginx.service  # 打开编辑器编辑 drop-in,退出后自动 daemon-reload
专有名词:单元名补全:省略后缀时默认补 .service(如 systemctl start sshd ≡ sshd.service);glob 匹配:单元命令支持 fnmatch 通配符;编辑调用:systemctl edit 通过 $SYSTEMD_EDITOR / $EDITOR / $VISUAL 选择编辑器(默认 fallback:editor→nano→vim→vi)。

3.3 包图 — 命令分组

systemctl 的全部命令可归为七大类,便于记忆与检索。

PlantUML diagram
图 8:systemctl 命令分组包图

归类速记

分组代表命令作用
Unit Commandslist-units / status / start / stop / set-property / kill单元的运行时管理
Unit File Commandslist-unit-files / enable / mask / edit / set-default单元文件的安装与自启
Job Commandslist-jobs / cancel任务队列管理
Environment Commandsshow-environment / set-environment管理器环境变量块
Manager State Commandsdaemon-reload / daemon-reexec / log-level管理器自身状态
System Commandsis-system-running / poweroff / reboot / suspend整机电源与运行级别
Machine Commandslist-machines容器与机器

3.4 类图 — 对象模型

理解 systemctl / systemd 背后的核心抽象:Manager、Unit、Job、UnitFile 及其关系。

PlantUML diagram
图 9:systemctl 对象模型类图

用例演示

systemctl show nginx.service -p MainPID -p ActiveState   # 读属性(脚本友好)
systemctl show nginx.service --value -p ExecStart        # 只输出值
systemctl list-dependencies nginx.service                # 递归列出依赖
systemctl list-dependencies --reverse nginx.service      # 谁依赖它
专有名词:Manager 即 systemd 守护进程在 D-Bus 上暴露的对象;Unit 是单元运行时对象,Service/Target/Timer 等都是其特化;Job 是一次待执行的状态转换;UnitFile 是磁盘上的配置文件(静态),可被 enable 成 enabled / disabled / masked / static 等状态。

4. 配置文件语法规范

单元文件(unit file)是 systemd 的"声明式配置",写对了才能跑稳。规范分三层:文件结构、值格式、每段的指令语义。本节是实战的基础,所有 service / timer / socket 的排查都从这里开始。

4.1 单元文件结构

单元文件由若干段(section)组成,段内是键=值对。段名写在方括号里且大小写敏感,# 或 ; 开头是注释,空行忽略,键=值 可用反斜杠续行。

PlantUML diagram
图 10:单元文件结构图(三个段 + 典型指令)

结构演示

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Server        # [Unit] 通用段:描述、依赖、排序
After=network-online.target
Wants=network-online.target

[Service]                                 # [Service] 服务特有段:执行、类型、重启
Type=simple
ExecStart=/usr/local/bin/myapp --port 8080
Restart=on-failure
User=www-data

[Install]                                 # [Install] 安装段:enable 时如何被拉起
WantedBy=multi-user.target
专有名词:[Unit] 是所有单元类型通用的段(描述、依赖、排序);[Install] 也是通用段,仅被 enable 使用;[Service] / [Timer] / [Socket] 等是各类型特有的段。`--full` 编辑主文件;普通 systemctl edit 只追加 drop-in。

4.2 值格式与占位符

先掌握值格式,再看指令才不会读错配置。

类型格式示例
时间(duration)数字+单位,可组合;单位 us/ms/s/min/h/d;infinity 永不超时30s、1min 30s、2h、500ms、infinity
大小(size)数字+单位,可带小数;K/M/G/T(以 1024 计,也可 KB/MB 表示 1000 计)10M、1.5G、512K
布尔yes/no/on/off/true/false/1/0PrivateTmp=yes
信号/退出码数字或 SIGxxx / 常量名Restart=on-failure、SuccessExitStatus=SIGUSR1

说明符(specifier)— 模板单元的核心

在单元文件值里,%X 会被替换为运行期信息,常用于模板单元 foo@.service(用 systemctl start foo@bar 实例化,实例名即 bar):

说明符含义
%n完整单元名(如 foo@bar.service)
%N未转义前的完整单元名
%p前缀名(去掉 @实例 和 .service,即 foo)
%P未转义的前缀名
%i实例名(模板单元中就是 @ 后那段,即 bar)
%j实例名的非转义形式
%t运行时目录(/run 或 $XDG_RUNTIME_DIR)
%h用户家目录
%u运行用户
%H主机名
%%转义出一个字面 %
# getty@tty1.service 的 [Service]
ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I $TERM   # %I 实例名 tty1
专有名词:模板单元(template unit) 文件名带 @(如 getty@.service),本身不可运行,须实例化为 getty@tty1.service 等;实例名用于区分多个同名服务,配合 %i 展开。

4.3 [Unit] 段 — 描述、依赖与排序

这是最容易写错的部分。依赖决定"要不要拉起来",排序决定"谁先谁后",两者是正交的。

PlantUML diagram
图 11:依赖(Requires/Wants)与排序(After/Before)对比图
指令类别说明
Description=描述一行说明,显示在 systemctl status 的标题里
Documentation=描述文档 URL 或 man: 页面(status 里可跳转)
After= / Before=排序只保证先后顺序,不自动拉入依赖
Wants=软依赖尽量启动;目标启动失败不影响本单元
Requires=硬依赖必须一起启动;依赖失败则本单元启动失败
Requisite=硬依赖依赖未激活则不启动本单元(但不主动拉起)
BindsTo=强绑定依赖停止/重启,本单元也随之停止/重启
Conflicts=互斥与本单元互斥,启动我即停它
PartOf=从属父单元停止/重启时带动本单元
OnFailure= / OnSuccess=回调失败/成功时触发另一个单元(如报警、清理)
StartLimitIntervalSec=
StartLimitBurst=
限速单位时间内允许启动次数,超限则拒绝(配合 StartLimitAction=)
RefuseManualStart=保护yes 时禁止手动 start/stop
RequiresMountsFor=挂载依赖自动为路径对应的挂载点建依赖与排序
最佳实践:需要"先启动依赖再启动我",要 Requires=/Wants= 与 After= 成对使用。只写 Requires= 不写 After=:会拉起但可能并行(不保证先就绪);只写 After= 不写依赖:保证顺序但不会自动拉起。

4.4 [Service] 段 — 命令与类型

4.4.1 执行命令

指令说明
ExecStart=启动命令(必要;Type=oneshot 可省略)
ExecStartPre= / ExecStartPost=启动前/后的钩子命令
ExecCondition=条件命令,非零退出则跳过启动(不算失败)
ExecStop=停止命令(默认发 SIGTERM 给主进程)
ExecReload=重载命令(systemctl reload 触发)

ExecStart 前缀修饰符(放在命令最前面):

前缀含义
-该命令失败/不存在也不计入失败(如 ExecStart=-/usr/bin/optional.sh)
+以 root 全特权执行,无视 User=/CapabilityBoundingSet=
!提升到(受限的)root 权限执行
@把可执行文件路径作为 argv[0] 传入(脚本解释器场景)
:环境变量不展开、前缀不解析(字面执行)

变量展开:$FOO 只展开一次(传给子进程仍保留字面量);${FOO} 在传给子进程前先替换为值。用 ${FOO} 更符合预期。

4.4.2 Type — systemd 如何判定"启动完成"

Type完成判定适用场景
simple主进程一 fork 出就算 active最常见;进程不通知就绪
execExecStart 的 exec() 成功即 active比 simple 更严谨(exec 失败即失败)
forking主进程 fork 出子进程并退出后 active传统 daemonize 的守护进程(配合 PIDFile=)
oneshot命令执行完即 active,随后变 inactive一次性任务(脚本、初始化),配 RemainAfterExit=yes 保持 active
dbus服务在 BusName= 上注册后才 activeD-Bus 服务
notify服务通过 sd_notify(READY=1) 上报就绪支持 sd_notify 的应用,最可靠
idle等所有任务排队完成后再启动(输出不混行)交互/串口服务
# forking 经典写法(老式守护进程)
[Service]
Type=forking
PIDFile=/run/daemon.pid
ExecStart=/usr/sbin/daemon -d
ExecReload=/bin/kill -HUP $MAINPID

# notify 推荐写法(现代服务,自身支持 sd_notify)
[Service]
Type=notify
NotifyAccess=all
ExecStart=/usr/local/bin/server

4.5 [Service] 段 — 重启策略、身份与沙箱

4.5.1 退出与重启

指令说明
Restart=重启策略,见下表
RestartSec=重启前等待时间(如 RestartSec=3),建议配退避
TimeoutStartSec= / TimeoutStopSec=启动/停止超时(默认 90s);停止超时会 SIGKILL
TimeoutSec=同时设置上述两者
SuccessExitStatus=额外视为成功的退出码/信号
RemainAfterExit=oneshot 执行完仍保持 active
WatchdogSec=启用硬件看门狗,服务须周期性 sd_notify(WATCHDOG=1)
Restart= 值何时重启
no永不自动重启(默认)
on-success干净退出(码 0 或 SuccessExitStatus)时
on-failure非零退出码、信号杀死、超时、watchdog 触发时(最常用)
on-abnormal被信号、超时、watchdog 终止时(不含非零退出码)
on-abort非干净信号导致异常退出时
on-watchdogwatchdog 超时未喂狗时
always任何退出都重启(包括正常退出)

4.5.2 身份与工作环境

指令说明
User= / Group=以指定用户/组运行(默认 root)
SupplementaryGroups=附加组
Environment=单行设置环境变量(可写多行)
EnvironmentFile=从文件读 KEY=VALUE;路径前加 - 表示文件缺失不报错
WorkingDirectory=工作目录(~ 相对 User 家目录)
UMask= / Nice=默认 umask / 调度优先级
StandardOutput= / StandardError=stdout/stderr 去向(默认进 journal,可 journal/file:/null)

4.5.3 资源限制与沙箱

指令说明
LimitNOFILE= / LimitNPROC= 等rlimit 资源上限(infinity 不设限),常见坑:服务句柄不够
CPUWeight= / MemoryMax=cgroup 资源控制(Weight 相对权重,Max 硬上限)
PrivateTmp=yes私有 /tmp(每个实例独立,安全)
PrivateDevices= / PrivateNetwork=隔离 /dev、网络命名空间
ProtectSystem=full|strict根目录只读;strict 连 /etc /usr 也只读
ProtectHome=yes/home /root /run/user 不可读
ReadOnlyPaths= / ReadWritePaths=指定目录只读/读写
NoNewPrivileges=yes禁止通过 setuid 提权
SystemCallFilter=系统调用白/黑名单(seccomp)
[Service]
User=www-data
EnvironmentFile=-/etc/myapp.env          # 缺失也不报错
LimitNOFILE=65535
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
Restart=on-failure
RestartSec=3
TimeoutStopSec=30

4.6 [Install] 段 — 开机自启

只在 systemctl enable 时读取,决定"开机时被谁拉起",与运行无关。

指令说明
WantedBy=multi-user.targetenable 时在 multi-user.target.wants/ 建软链接(软依赖,最常见)
RequiredBy=建到 .requires/(硬依赖)
Also=enable 本单元时一并 enable 其他单元(如配套 timer)
Alias=为本单元建立别名符号链接
DefaultInstance=模板单元无显式实例时的默认实例名
[Install]
WantedBy=multi-user.target     # 开机进入多用户模式时自动启动
Also=myapp.timer               # enable myapp.service 时顺带 enable 定时器

4.7 其他单元类型段

[Timer] — 定时器(cron 替代)

指令说明
OnCalendar=*-*-* 03:00:00日历式定时(每周一 Mon *-*-* 09:00:00、每小时 *:00:00)
OnBootSec=5min开机后 N 时间触发
OnUnitActiveSec=1h目标单元上次激活后 N 时间触发(循环)
Persistent=yes关机期间错过的触发,开机后补执行
AccuracySec=1min允许的执行误差(省电合并唤醒)
RandomizedDelaySec=随机延迟避免惊群
Unit=到点激活的目标单元(默认同名 .service)

[Socket] — 套接字(按需启动)

指令说明
ListenStream=TCP 端口或 UNIX 套接字路径
ListenDatagram=UDP
Accept=yes每连接实例化一个服务实例(否则首次连接即拉起服务)
Service=被激活的服务(默认同名 .service)
SocketMode=UNIX 套接字权限

[Path]、[Mount]、[Slice]

段关键指令说明
[Path]PathExists= / PathChanged= / DirectoryNotEmpty=文件/目录出现或变化时触发服务
[Mount]What= / Where= / Type= / Options=声明挂载点(文件名即 Where 转义后的路径.mount)
[Slice]CPUWeight= / MemoryMax=资源分组,可在 service.slice 上聚合配额

4.8 drop-in 覆盖与优先级

不要直接改厂商自带的 /usr/lib/systemd/system/*.service。用 drop-in 追加覆盖:

# 方法一:systemctl edit(自动创建并打开编辑器,保存后自动 daemon-reload)
systemctl edit myapp.service

# 方法二:手工创建
sudo mkdir -p /etc/systemd/system/myapp.service.d
sudo tee /etc/systemd/system/myapp.service.d/override.conf <<'EOF'
[Service]
Environment=APP_ENV=production
RestartSec=5
EOF
sudo systemctl daemon-reload

叠加规则(栈式):同一键的取值按目录优先级取最高者,目录优先级 /etc > /run > /usr/lib;同一目录内按文件名排序,后者覆盖前者。要清空某个在低优先级文件里设置的键,赋空值即可:

# override.conf —— 清掉上游设置的 User
[Service]
User=
改动后必做:systemctl daemon-reload 重载管理器,否则运行的是旧配置;改完用 systemctl cat myapp.service 确认生效后的合并结果;用 systemd-analyze verify /etc/systemd/system/myapp.service 做语法/依赖校验,能在启动前发现 80% 的笔误。

5. 实战速查手册

5.1 查询与状态

命令说明
systemctl / systemctl list-units列出内存中已加载的单元(默认只显 active/失败)
systemctl list-units --all包含未激活单元
systemctl list-unit-files列出磁盘上所有单元文件及 enable 状态
systemctl --type=service --state=failed只列失败的服务
systemctl status <unit>单元详情 + 最近日志(-n 控制行数)
systemctl status PID按 PID 找它属于哪个单元
systemctl show <unit> -p <prop>读取机器可解析属性
systemctl cat <unit>打印单元文件内容(fragment + drop-ins)
systemctl is-active <unit>仅判断是否 active(0=是)
systemctl is-failed <unit>仅判断是否 failed(0=是)
systemctl is-enabled <unit>仅判断自启状态
systemctl is-system-running系统运行状态(running/degraded/...)
systemctl list-dependencies <unit>递归列出依赖树
systemctl list-jobs当前进行中的 job
systemctl list-timers定时器下次触发时间
systemctl list-sockets套接字单元监听状态

5.2 服务生命周期

命令说明
systemctl start <unit>启动(激活)
systemctl stop <unit>停止(SIGTERM→超时 SIGKILL)
systemctl restart <unit>停止后重新启动
systemctl try-restart <unit>仅在运行中才重启
systemctl reload <unit>仅重载服务自身配置(不重启进程)
systemctl reload-or-restart <unit>支持 reload 则 reload,否则 restart
systemctl kill <unit> -s SIGKILL向单元进程发信号(--kill-whom=main/all)
systemctl reset-failed <unit>清除 failed 状态并重置重启计数/速率限制
systemctl freeze <unit> / thaw用 cgroup freezer 挂起/恢复进程
systemctl clean <unit> --what=all清除 State/Cache/Logs 等目录数据(需先 stop)
systemctl isolate <target>切换运行级别,停止其余所有单元(危险)

5.3 开机自启管理

命令说明
systemctl enable <unit>设置开机自启(建符号链接)
systemctl enable --now <unit>自启 + 立即启动
systemctl disable <unit>取消自启(可加 --now 一并停止)
systemctl reenable <unit>disable 后再 enable,重置链接到默认
systemctl mask <unit>彻底屏蔽(链接到 /dev/null)
systemctl unmask <unit>解除屏蔽
systemctl preset <unit>按 preset 策略重置自启状态
systemctl get-default查看默认启动 target
systemctl set-default multi-user.target设置默认启动 target(图形→命令行)
systemctl edit <unit>编辑 drop-in(--full 改主文件,--runtime 只对本次开机)
systemctl revert <unit>撤销 edit / set-property / mask 的改动

5.4 系统电源管理

命令说明
systemctl poweroff关机断电
systemctl reboot重启
systemctl halt停机但不断电
systemctl kexec用 kexec 快速重启内核
systemctl suspend挂起(睡眠)
systemctl hibernate休眠到磁盘
systemctl hybrid-sleep混合挂起+休眠
systemctl rescue进入救援模式(单用户)
systemctl emergency进入紧急模式(只挂载根文件系统)
systemctl reboot --when=21:00定时重启(--when=cancel 取消)
systemctl poweroff -f强制关机(跳过服务关闭,数据可能丢失)

5.5 环境与属性

命令说明
systemctl show-environment查看管理器环境变量块
systemctl set-environment VAR=val设置管理器环境变量
systemctl unset-environment VAR删除管理器环境变量
systemctl set-property foo.service CPUWeight=200 MemoryMax=2G运行时调整资源控制并写盘持久化
systemctl set-property --runtime ...同上但仅本次开机有效
systemctl daemon-reload单元文件改动后重载全部配置与依赖树
systemctl daemon-reexec重执行 systemd 进程(升级用)
systemctl log-level debug调整 systemd 日志级别(排障用)

5.6 定时任务与套接字

systemd 的 cron 替代是 timer 单元;按需启动的守护进程用 socket 单元。

创建一个定时任务

# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily

[Timer]
OnCalendar=*-*-* 03:00:00   # 每天凌晨 3 点
Persistent=true             # 错过(关机期间)后开机补跑

[Install]
WantedBy=timers.target

systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers backup.timer
命令说明
systemctl list-timers查看所有定时器及下次触发
systemctl start <name>.timer启用某个定时器
systemctl list-sockets查看套接字单元(如 ssh.socket)

5.7 日志查看

systemctl 本身不读日志,配合 journalctl 使用:

journalctl -u nginx.service          # 该单元全部日志
journalctl -u nginx.service -f       # 实时跟踪
journalctl -u nginx.service --since "1 hour ago"
journalctl -b -p err                 # 本次开机以来的错误级日志
journalctl --since today             # 今天所有日志
journalctl -f                        # 实时看全部日志

5.8 新建服务实战

例:把 Go/Python 写的 Web 后端托管为系统服务

sudo tee /etc/systemd/system/myapp.service <<'EOF'
[Unit]
Description=My Application Server
After=network-online.target
Wants=network-online.target

[Service]
Type=notify                      # 或 simple
User=www-data
WorkingDirectory=/srv/myapp
ExecStart=/usr/local/bin/myapp-server --config /etc/myapp.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure               # 崩溃自动拉起
RestartSec=3
LimitNOFILE=65535
EnvironmentFile=-/etc/myapp.env

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

改配置的标准流程

systemctl edit myapp.service     # 追加 drop-in(不污染原文件)
# 或直接改 /etc/systemd/system/myapp.service
systemctl daemon-reload          # 关键!改完必须重载
systemctl restart myapp.service

5.9 故障排查套路

# 1. 服务起不来 → 看状态与日志
systemctl status myapp.service
journalctl -u myapp.service -e   # -e 跳到末尾

# 2. 查看系统是否 degraded(有单元失败)
systemctl is-system-running
systemctl --state=failed         # 列出所有失败单元
systemctl list-unit-files --state=enabled

# 3. 查看启动耗时,找最慢的服务
systemd-analyze blame
systemd-analyze critical-chain

# 4. 单元文件语法错误 / 依赖环
systemd-analyze verify /etc/systemd/system/myapp.service
systemctl list-jobs --after --before   # 看 job 互相等待

# 5. 改完单元文件没生效?
systemctl daemon-reload           # 十有八九忘了这步
systemctl status myapp.service -n 50   # 看最近 50 行日志
排障要诀:先看 Active: 行是 failed 还是 inactive;再看 Loaded: 行是不是 not-found / masked / error(bad-setting);最后看日志里有没有 Failed to parse、Cannot open、Permission denied。改单元文件后必须 daemon-reload。

4.10 退出码(LSB)

值systemd 中的含义
0单元处于 active / enabled
1单元 not failed(is-failed 用)
3单元 not active(is-active 用)
4不存在该单元
非 0操作失败